functor TypeCheckerFun (type T)= struct

structure SymTab = SymTabFun(type T = T)

val init_size= 128

val hash= HashString.hashString
          
val cmp:string * string->bool = (op =)

exception not_found
		   
val functionTable:(string,(T*T list)) HashTable.hash_table  = 
    HashTable.mkTable (hash,cmp) (init_size,not_found)

val structTable:(string,(string*T) list) HashTable.hash_table =
    HashTable.mkTable (hash,cmp) (init_size,not_found)

fun addProto (name,(retType,arguments))=
    HashTable.insert functionTable (name,(retType,arguments))

fun findProto name =
    HashTable.find functionTable name

fun addStruct (name,fields)=
    HashTable.insert structTable (name,fields)

fun findStruct name =
    HashTable.find structTable name

open Error

val errors : error list ref = ref []

fun addError e = errors:= e::(!errors)

fun resetError () = errors := []

fun elabProg (prog:Ast.Program) =
    let val _ = resetError()
	val _ = SymTab.resetSymTab()
	val (protolist,structlist,funclist)=prog
    in
	(
	 List.app elabProto protolist;
	 List.app elabStruct structlist;
	 List.app elabFunction funclist;
	 !errors
	)
    end

and elabProto (proto:Ast.FunctionProto) =
    let val (t,name,args,_) = proto
    in
	(* Add prototype information to functionTable *)
	()
    end

and elabStruct (struc:Ast.Struct) =
    let val (name,varlist,_) = struc
    in
	(* Add struct information to structTable *)
	()
    end

and elabFunction (func:Ast.Function) =
    let val (proto,block,_)=func
	val (t,name,args,_)=proto
    in
	(
	 elabProto proto;
	 List.app elabVardecl args;
	 elabBlock block
	)
    end

and elabBlock (Ast.Block(decls,stms,_))=
    (
     SymTab.beginScope();
     List.app elabVardecl decls;
     List.app elabStm stms;
     SymTab.endScope()
    )

and elabVardecl (decl:Ast.VarDecl)=
    (* add type of the variable to SymTab *)
    ()

and elabStm (stm:Ast.Stm) =
    case stm of 
	Ast.Assign(lval,exp,_) =>
	()
      (* let val t1=elabLval lval *)
      (* 	val t2=elabExp exp *)
      (* in *)
      (* 	if(typeMatch t1 t2) then () *)
      (* 	else *)
      (* 	    addError TypeMismatch *)
      (* end	 *)
      | Ast.If(exp,block1,block2,_) =>
	()
      (* let val t = elabExp exp  *)
      (* in *)
      (* 	( *)
      (* 	 if(IsBool t) then *)
      (* 	     ( *)
      (* 	      elabBlock block1; *)
      (* 	      elabBlock block2 *)
      (* 	     ) *)
      (* 	 else *)
      (* 	     addError TypeMismatch *)
      (* 	) *)
      (* end *)
      | _ => ()

and typeMatch (t1:T)(t2:T):bool = true

and IsBool (t:T):bool = true

(* and elabLval:Ast.Lval -> T *)

(* and elabExp:Ast.Exp -> T *)

end
